package com.example.load_dex;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.ArrayMap;

import java.io.File;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import dalvik.system.DexClassLoader;
import dalvik.system.PathClassLoader;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        /** 动态加载dex
         * arg1 获取app私有目录
         * arg2 dex文件路径
         * 注意：没有动态去申请存储权限需要手动添加
         **/
//        Context appContext = this.getApplicationContext();
//        startTestActivityFirstMethod(this, "/sdcard/3.dex");
//        Load_dex.testDexClassLoader(appContext,"/sdcard/3.dex");
    }
    /** 反射替换
     *
     *
     *
     **/
    public void replaceClassloader(ClassLoader classloader){
        try {
            Class<?> ActivityThreadClazz=classloader.loadClass("android.app.ActivityThread");
            Method currentActivityThreadMethod= ActivityThreadClazz.getDeclaredMethod("currentActivityThread");
            currentActivityThreadMethod.setAccessible(true);
            Object activityThreadObj=currentActivityThreadMethod.invoke(null);
            //final ArrayMap<String, WeakReference<LoadedApk>> mPackages = new ArrayMap<>();
            Field mPackagesField=ActivityThreadClazz.getDeclaredField("mPackages");
            mPackagesField.setAccessible(true);
            ArrayMap mPackagesObj= (ArrayMap) mPackagesField.get(activityThreadObj);
            WeakReference wr= (WeakReference) mPackagesObj.get(this.getPackageName());
            Object loadedApkObj=wr.get();

            Class LoadedApkClazz=classloader.loadClass("android.app.LoadedApk");
            //private ClassLoader mClassLoader;
            Field mClassLoaderField=LoadedApkClazz.getDeclaredField("mClassLoader");
            mClassLoaderField.setAccessible(true);
            mClassLoaderField.set(loadedApkObj,classloader);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }


    }

    /** Dexclassloader 动态加载Activity
     *
     **/
    public void startTestActivityFirstMethod(Context context, String dexfilepath) {
        File optfile = context.getDir("opt_dex", 0); //在app私有目录新建文件opt_dex 用于存放dex文件
        File libfile = context.getDir("lib_path", 0);//在app私有目录新建文件，用于存放so文件
        //  ClassLoader parent获取的两种方式
        ClassLoader parentClassloader = MainActivity.class.getClassLoader();
        ClassLoader tmpClassLoader = context.getClassLoader();
        /** 加载sdcard下的dex
         * arg1 dex文件路径
         * arg2 app私有目录文件opt_dex路径
         * arg3 app私有目录文件lib_path路径
         * arg4 parentClassloader 父类装载器
         **/
        DexClassLoader dexClassLoader = new DexClassLoader(dexfilepath, optfile.getAbsolutePath(), libfile.getAbsolutePath(), parentClassloader);
        replaceClassloader(dexClassLoader);
        Class<?> clazz = null;
        try {
            //加载类
            clazz = dexClassLoader.loadClass("com.example.test_dex.TestActivity");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        if (clazz != null) {
            context.startActivity(new Intent(context, clazz));

        }
    }

    /** Pathclassloader 动态加载Activity测试
     *  Pathclassloader与 testDexClassLoader  在android 高版本同样使用
     **/
    public void testPathClassLoader(Context context, String dexfilepath) {
        ClassLoader parentClassloader = MainActivity.class.getClassLoader();
        PathClassLoader pathClassLoader = new PathClassLoader(dexfilepath, parentClassloader);
        replaceClassloader(pathClassLoader);
        Class<?> clazz = null;
        try {
            //加载类
            clazz = pathClassLoader.loadClass("com.example.test_dex.TestActivity");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        if (clazz != null) {
            context.startActivity(new Intent(context, clazz));
        }

    }

}